

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=&#34;auto&#34;>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.png">
  <link rel="icon" href="/img/favicon.png">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="4.1-4.2 栈的定义4.2.1 栈的定义栈是限定仅在表尾进行插入和删除操作的线性表。我们把允许插入和删除的一端称为栈顶(top)，另一端称为栈底(bottom)，不含任何数据元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表，简尔LIFO结构。它的特殊之处就在于限制了这个线性表的插入和删除位置，它始终只在栈顶进行。这也就使得：栈底是固定的，最先进栈的只能在栈底。栈">
  <meta name="author" content="closer">
  <meta name="keywords" content="">
  
  <title>大话数据结构第四章 栈与队列 - closer的自留地</title>

  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/lib/hint/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10.6.0/styles/androidstudio.min.css" />
    
  

  
    <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
  



<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_kmeydafke9r.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    var CONFIG = {"hostname":"blog.zsaa.top","root":"/","version":"1.8.10","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"right","visible":"always","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"copy_btn":true,"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"baidu":"608f2baddd361128381ad2bf9377bf89","google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":"YzLqNtMw1YEwwACli1FUsIUM-gzGzoHsz","app_key":"HLUt5izfTvTcbEbOrA59W92a","server_url":"https://yzlqntmw.lc-cn-n1-shared.com"}}};
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 5.4.0"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/">&nbsp;<strong>Hello</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/links/">
                <i class="iconfont icon-link-fill"></i>
                友链
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" data-toggle="modal" data-target="#modalSearch">&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;</a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner" id="banner" parallax=true
         style="background: url('/img/default.jpg') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="page-header text-center fade-in-up">
            <span class="h2" id="subtitle" title="大话数据结构第四章 栈与队列">
              
            </span>

            
              <div class="mt-3">
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2019-11-28 00:00" pubdate>
        2019年11月28日 凌晨
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      5.3k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      61
       分钟
    </span>
  

  
  
    
      <!-- LeanCloud 统计文章PV -->
      <span id="leancloud-page-views-container" class="post-meta" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="leancloud-page-views"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">大话数据结构第四章 栈与队列</h1>
            
              <p class="note note-info">
                
                  本文最后更新于：2020年2月19日 晚上
                
              </p>
            
            <div class="markdown-body">
              <h2 id="4-1-4-2-栈的定义"><a href="#4-1-4-2-栈的定义" class="headerlink" title="4.1-4.2 栈的定义"></a>4.1-4.2 栈的定义</h2><h3 id="4-2-1-栈的定义"><a href="#4-2-1-栈的定义" class="headerlink" title="4.2.1 栈的定义"></a>4.2.1 栈的定义</h3><p><strong>栈是限定仅在表尾进行插入和删除操作的线性表。</strong><br>我们把允许插入和删除的一端称为栈顶(top)，另一端称为栈底(bottom)，不含任何数据元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表，简尔LIFO结构。<br>它的特殊之处就在于限制了这个线性表的插入和删除位置，它始终只在栈顶进行。这也就使得：栈底是固定的，最先进栈的只能在栈底。<br>栈的插入操作，叫作进栈，也称压栈、入栈(push)。  </p>
<span id="more"></span>

<p>栈的删除操作，叫作出栈，也有的叫作弹栈(pop)。  </p>
<h2 id="4-3-栈的抽象数据类型"><a href="#4-3-栈的抽象数据类型" class="headerlink" title="4.3 栈的抽象数据类型"></a>4.3 栈的抽象数据类型</h2><figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c">ADT 栈(<span class="hljs-built_in">stack</span>)<br>Data<br>    同线性表。元素具有相同的类型，相邻元素具有前驱和后继关系。<br><span class="hljs-function">Operation</span><br><span class="hljs-function">    <span class="hljs-title">InitStack</span><span class="hljs-params">(*s)</span>：初始化操作，建立一个空栈s。</span><br><span class="hljs-function">    <span class="hljs-title">DestroyStack</span><span class="hljs-params">(*s)</span>：若楼存在，则销毁它。</span><br><span class="hljs-function">    <span class="hljs-title">ClearStack</span><span class="hljs-params">(*s)</span>：将栽清空。</span><br><span class="hljs-function">    <span class="hljs-title">StackEmpty</span><span class="hljs-params">(S)</span>：若为空，返回<span class="hljs-literal">true</span>，否则返回<span class="hljs-literal">false</span>。</span><br><span class="hljs-function">    <span class="hljs-title">GetTop</span><span class="hljs-params">(s，*e)</span>：若栽存在且非空，用e返回s的栽顶元素。</span><br><span class="hljs-function">    <span class="hljs-title">Push</span><span class="hljs-params">(*s，e)</span>：若栈S存在，插入新元素e到栈S中并成为栈顶元素。</span><br><span class="hljs-function">    <span class="hljs-title">Pop</span><span class="hljs-params">(*S，*e)</span>：删除栈S中栈顶元素，并用e返回其值。</span><br><span class="hljs-function">    <span class="hljs-title">StackLength</span><span class="hljs-params">(s)</span>：返回栈s的元素个数。</span><br><span class="hljs-function">endADT</span><br></code></pre></div></td></tr></table></figure>

<h2 id="4-4-栈的顺序存储结构及实现"><a href="#4-4-栈的顺序存储结构及实现" class="headerlink" title="4.4 栈的顺序存储结构及实现"></a>4.4 栈的顺序存储结构及实现</h2><h3 id="4-4-1-栈的顺序存储结构"><a href="#4-4-1-栈的顺序存储结构" class="headerlink" title="4.4.1 栈的顺序存储结构"></a>4.4.1 栈的顺序存储结构</h3><p>栈是线性表的特例，那么栈的顺序存储其实也是线性表顺序存储的简化，我们简称为顺序栈。线性表是用数组来实现的。<br>我们定义一个top变量来指示栈顶元素在数组中的位置，它可以来回移动，意味着栈顶的top可以变大变小，但无论如何游标不能超出栈的长度。同理，若存储栈的长度为StackSize，则栈顶位置top必须小于StackSize。当栈存在一个元素时，top等于0，因此通常把空栈的判定条件定为top等于-1。</p>
<p>栈的结构定义：  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">int</span> SElemType; <span class="hljs-comment">/* SElemType类型根据实际情况而定，这里假设为int */</span><br><span class="hljs-comment">/* 顺序栈结构 */</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span></span><br><span class="hljs-class">&#123;</span><br>        SElemType data[MAXSIZE];<br>        <span class="hljs-keyword">int</span> top; <span class="hljs-comment">/* 用于栈顶指针 */</span><br>&#125;SqStack;<br></code></pre></div></td></tr></table></figure>

<p>若现在有一个栈，StackSize是5，则栈普通情况、空栈和栈满的情况示意图如图4-4-2所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-4-2.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-4-2">  </p>
<h3 id="4-4-2-栈的顺序存储结构——进栈操作"><a href="#4-4-2-栈的顺序存储结构——进栈操作" class="headerlink" title="4.4.2 栈的顺序存储结构——进栈操作"></a>4.4.2 栈的顺序存储结构——进栈操作</h3><p>对于栈的插入，即进栈操作，其实就是在栈顶插入一个元素。<br>进栈操作push，其代码如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 插入元素e为新的栈顶元素 */</span><br><span class="hljs-function">Status <span class="hljs-title">Push</span><span class="hljs-params">(SqStack *S,SElemType e)</span></span><br><span class="hljs-function"></span>&#123;<br>        <span class="hljs-keyword">if</span>(S-&gt;top == MAXSIZE <span class="hljs-number">-1</span>) <span class="hljs-comment">/* 栈满 */</span><br>        &#123;<br>            <span class="hljs-keyword">return</span> ERROR;<br>        &#125;<br>        S-&gt;top++;   <span class="hljs-comment">/* 栈顶指针增加一 */</span><br>        S-&gt;data[S-&gt;top]=e;  <span class="hljs-comment">/* 将新插入元素赋值给栈顶空间 */</span><br>        <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h3 id="4-4-3-栈的顺序存储结构——出栈操作"><a href="#4-4-3-栈的顺序存储结构——出栈操作" class="headerlink" title="4.4.3 栈的顺序存储结构——出栈操作"></a>4.4.3 栈的顺序存储结构——出栈操作</h3><p>出栈操作pop，代码如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 若栈不空，则删除S的栈顶元素，用e返回其值，并返回OK；否则返回ERROR */</span><br><span class="hljs-function">Status <span class="hljs-title">Pop</span><span class="hljs-params">(SqStack *S,SElemType *e)</span></span><br><span class="hljs-function"></span>&#123;<br>        <span class="hljs-keyword">if</span>(S-&gt;top==<span class="hljs-number">-1</span>)<br>                <span class="hljs-keyword">return</span> ERROR;<br>        *e=S-&gt;data[S-&gt;top]; <span class="hljs-comment">/* 将要删除的栈顶元素赋值给e */</span><br>        S-&gt;top--;   <span class="hljs-comment">/* 栈顶指针减一 */</span><br>        <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>两者没有涉及到任何循环语句，因此时间复杂度均是O(1)。  </p>
<h2 id="4-5-两栈共享空间"><a href="#4-5-两栈共享空间" class="headerlink" title="4.5 两栈共享空间"></a>4.5 两栈共享空间</h2><p>如果我们有两个相同类型的栈，我们为它们各自开辟了数组空间，极有可能是第一个栈已经满了，再进栈就溢出了，而另一个栈还有很多存储空间空闲。这又何必呢？我们完全可以用一个数组来存储两个栈，只不过需要点小技巧。<br>数组有两个端点，两个栈有两个栈底，让一个栈的栈底为数组的始端，即下标为0处，另一个栈为栈的末端，即下标为数组长度n-1处。这样，两个栈如果增加元素，就是两端点向中间延伸。<br>其实关键思路是：它们是在数组的两端，向中间靠拢。top1和top2是栈1和栈2的栈顶指针，可以想象，只要它们俩不见面，两个栈就可以一直使用。<br>从这里也就可以分析出来，栈1为空时，就是top1等于-1时；而当top2等于n时，即是栈2为空时，那什么时候栈满呢？<br>想想极端的情况，若栈2是空栈，栈1的top1等于n-1时，就是栈1满了。反之，当栈1为空栈时，top2等于0时，为栈2满。但更多的情况，其实就是我刚才说的，两个栈见面之时，也就是两个指针之间相差1时，即top1+1==top2为栈满。<br>两栈共享空间的结构的代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 两栈共享空间结构 */</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span></span><br><span class="hljs-class">&#123;</span><br>    SElemType data[MAXSIZE];<br>    <span class="hljs-keyword">int</span> top1;    <span class="hljs-comment">/* 栈1栈顶指针 */</span><br>    <span class="hljs-keyword">int</span> top2;    <span class="hljs-comment">/* 栈2栈顶指针 */</span><br>&#125;SqDoubleStack;<br></code></pre></div></td></tr></table></figure>

<p>对于两栈共享空间的push方法，我们除了要插入元素值参数外，还需要有一个判断是栈1还是栈2的栈号参数stackNumber。插入元素的代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 插入元素e为新的栈顶元素 */</span><br><span class="hljs-function">Status <span class="hljs-title">Push</span><span class="hljs-params">(SqDoubleStack *S, SElemType e, <span class="hljs-keyword">int</span> stackNumber)</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">if</span> (S-&gt;top1 + <span class="hljs-number">1</span> == S-&gt;top2)    <span class="hljs-comment">/* 栈已满，不能再push新元素了 */</span><br>        <span class="hljs-keyword">return</span> ERROR;<br>    <span class="hljs-keyword">if</span> (stackNumber == <span class="hljs-number">1</span>)            <span class="hljs-comment">/* 栈1有元素进栈 */</span><br>        S-&gt;data[++S-&gt;top1] = e; <span class="hljs-comment">/* 若是栈1则先top1+1后给数组元素赋值。 */</span><br>    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stackNumber == <span class="hljs-number">2</span>)    <span class="hljs-comment">/* 栈2有元素进栈 */</span><br>        S-&gt;data[--S-&gt;top2] = e; <span class="hljs-comment">/* 若是栈2则先top2-1后给数组元素赋值。 */</span><br>    <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>因为在开始已经判断了是否有栈满的情况，所以后面的top1+1或top2-1是不担心溢出问题的。<br>对于两栈共享空间的pop方法，参数就只是判断栈1栈2的参数stackNumber，代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 若栈不空，则删除S的栈顶元素，用e返回其值，并返回OK；否则返回ERROR */</span><br><span class="hljs-function">Status <span class="hljs-title">Pop</span><span class="hljs-params">(SqDoubleStack *S, SElemType *e, <span class="hljs-keyword">int</span> stackNumber)</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">if</span> (stackNumber == <span class="hljs-number">1</span>)<br>    &#123;<br>        <span class="hljs-keyword">if</span> (S-&gt;top1 == <span class="hljs-number">-1</span>)<br>            <span class="hljs-keyword">return</span> ERROR; <span class="hljs-comment">/* 说明栈1已经是空栈，溢出 */</span><br>        *e = S-&gt;data[S-&gt;top1--]; <span class="hljs-comment">/* 将栈1的栈顶元素出栈 */</span><br>    &#125;<br>    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stackNumber == <span class="hljs-number">2</span>)<br>    &#123;<br>        <span class="hljs-keyword">if</span> (S-&gt;top2 == MAXSIZE)<br>            <span class="hljs-keyword">return</span> ERROR; <span class="hljs-comment">/* 说明栈2已经是空栈，溢出 */</span><br>        *e = S-&gt;data[S-&gt;top2++]; <span class="hljs-comment">/* 将栈2的栈顶元素出栈 */</span><br>    &#125;<br>    <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>事实上，使用这样的数据结构，通常都是当两个栈的空间需求有相反关系时，也就是一个栈增长时另一个栈在缩短的情况。就像买卖股票一样，你买入时，一定是有一个你不知道的人在做卖出操作。有人赚钱，就一定是有人赔钱。这样使用两栈共享空间存储方法才有比较大的意义。否则两个栈都在不停地增长，那很快就会因栈满而溢出了。  </p>
<h2 id="4-6-栈的链式存储结构及实现"><a href="#4-6-栈的链式存储结构及实现" class="headerlink" title="4.6 栈的链式存储结构及实现"></a>4.6 栈的链式存储结构及实现</h2><h3 id="4-6-1-栈的链式存储结构"><a href="#4-6-1-栈的链式存储结构" class="headerlink" title="4.6.1 栈的链式存储结构"></a>4.6.1 栈的链式存储结构</h3><p>栈的链式存储结构，简称为链栈。<br>链栈的结构代码如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 链栈结构 */</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StackNode</span></span><br><span class="hljs-class">&#123;</span><br>    SElemType data;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StackNode</span> *<span class="hljs-title">next</span>;</span><br>&#125;StackNode,*LinkStackPtr;<br><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LinkStack</span></span><br><span class="hljs-class">&#123;</span><br>    LinkStackPtr top;<br>    <span class="hljs-keyword">int</span> count;<br>&#125;LinkStack;<br></code></pre></div></td></tr></table></figure>

<h3 id="4-6-2-栈的链式存储结构-进栈操作"><a href="#4-6-2-栈的链式存储结构-进栈操作" class="headerlink" title="4.6.2 栈的链式存储结构-进栈操作"></a>4.6.2 栈的链式存储结构-进栈操作</h3><p>对于链栈的进栈push操作，假设元素值为e的新结点是s，top为栈顶指针，示意图如图4-6-2所示代码如下。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-6-2.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-6-2">  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 插入元素e为新的栈顶元素 */</span><br><span class="hljs-function">Status <span class="hljs-title">Push</span><span class="hljs-params">(LinkStack *S,SElemType e)</span></span><br><span class="hljs-function"></span>&#123;<br>    LinkStackPtr s=(LinkStackPtr)<span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(StackNode));<br>    s-&gt;data=e;<br>    s-&gt;next=S-&gt;top;<span class="hljs-comment">/* 把当前的栈顶元素赋值给新结点的直接后继，见图中① */</span><br>    S-&gt;top=s;         <span class="hljs-comment">/* 将新的结点s赋值给栈顶指针，见图中② */</span><br>    S-&gt;count++;<br>    <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h3 id="4-6-3-栈的链式存储结构——出栈操作"><a href="#4-6-3-栈的链式存储结构——出栈操作" class="headerlink" title="4.6.3 栈的链式存储结构——出栈操作"></a>4.6.3 栈的链式存储结构——出栈操作</h3><p>至于链栈的出栈pop操作，也是很简单的三句操作。假设变量p用来存储要删除的栈顶结点，将栈顶指针下移一位，最后释放p即可，如图4-6-3所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-6-3.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-6-3">  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 若栈不空，则删除S的栈顶元素，用e返回其值，并返回OK；否则返回ERROR */</span><br><span class="hljs-function">Status <span class="hljs-title">Pop</span><span class="hljs-params">(LinkStack *S,SElemType *e)</span></span><br><span class="hljs-function"></span>&#123;<br>        LinkStackPtr p;<br>        <span class="hljs-keyword">if</span>(StackEmpty(*S))<br>                <span class="hljs-keyword">return</span> ERROR;<br>        *e=S-&gt;top-&gt;data;<br>        p=S-&gt;top;               <span class="hljs-comment">/* 将栈顶结点赋值给p，见图中③ */</span><br>        S-&gt;top=S-&gt;top-&gt;next;    <span class="hljs-comment">/* 使得栈顶指针下移一位，指向后一结点，见图中④ */</span><br>        <span class="hljs-built_in">free</span>(p);                    <span class="hljs-comment">/* 释放结点p */</span><br>        S-&gt;count--;<br>        <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>链栈的进栈push和出栈pop操作都很简单，时间复杂度均是O(1)。<br>对比一下顺序栈与链栈，它们在时间复杂度上是一样的，均为O(1)。对于空间性能，顺序栈需要事先确定一个固定的长度，可能会存在内存空间浪费的问题，但它的优势是存取时定位很方便，而链栈则要求每个元素都有指针域，这同时也增加了一些内存开销，但对于栈的长度无限制。所以它们的区别是如果栈的使用过程中元素变化不可预料，有时很小，有时非常大，那么最好是用链栈，反之，如果它的变化在可控范围内，建议使用顺序栈会更好一些。  </p>
<h2 id="4-7-栈的作用"><a href="#4-7-栈的作用" class="headerlink" title="4.7 栈的作用"></a>4.7 栈的作用</h2><p>栈的引入简化了程序设计的问题，划分了不同关注层次，使得思考范围缩小，更加聚焦于我们要解决的问题核心。反之，像数组等，因为要分散精力去考虑数组的下标增减等细节问题，反而掩盖了问题的本质。  </p>
<h2 id="4-8-栈的应用——递归"><a href="#4-8-栈的应用——递归" class="headerlink" title="4.8 栈的应用——递归"></a>4.8 栈的应用——递归</h2><h3 id="4-8-1-4-8-2递归定义"><a href="#4-8-1-4-8-2递归定义" class="headerlink" title="4.8.1-4.8.2递归定义"></a>4.8.1-4.8.2递归定义</h3><p>我们<strong>把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数，称做递归函数</strong>。<br>当然，写递归程序最怕的就是陷入永不结束的无穷递归中，所以，<strong>每个递归定义必须至少有一个条件，满足时递归不再进行，即不再引用自身而是返回值退出</strong>。  </p>
<h2 id="4-9-栈的应用——四则运算表达式求值"><a href="#4-9-栈的应用——四则运算表达式求值" class="headerlink" title="4.9 栈的应用——四则运算表达式求值"></a>4.9 栈的应用——四则运算表达式求值</h2><h3 id="4-9-1-后缀-逆波兰-表示法定义"><a href="#4-9-1-后缀-逆波兰-表示法定义" class="headerlink" title="4.9.1 后缀(逆波兰)表示法定义"></a>4.9.1 后缀(逆波兰)表示法定义</h3><p>栈的现实应用也很多，我们再来重点讲一个比较常见的应用：数学表达式的求值。<br>一种不需要括号的后缀表达法，我们也把它称为逆波兰(Reverse Polish Notation，RPN)表示。<br>我们先来看看，对于“<code>9+(3-1)×3+10÷2</code>”，如果要用后缀表示法应该是：“<code>9 3 1-3*+10 2/+</code>”，这样的表达式称为后缀表达式，叫后缀的原因在于<strong>所有的符号都是在要运算数字的后面出现</strong>。  </p>
<h3 id="4-9-2-后缀表达式计算结果"><a href="#4-9-2-后缀表达式计算结果" class="headerlink" title="4.9.2 后缀表达式计算结果"></a>4.9.2 后缀表达式计算结果</h3><p>后缀表达式：<code>9 3 1-3*+10 2/+</code><br>规则：<strong>从左到右遍历表达式的每个数字和符号，遇到是数字就进栈，遇到是符号，就将处于栈顶两个数字出栈，进行运算，运算结果进栈，一直到最终获得结果。</strong>  </p>
<h3 id="4-9-3-中缀表达式转后缀表达式"><a href="#4-9-3-中缀表达式转后缀表达式" class="headerlink" title="4.9.3 中缀表达式转后缀表达式"></a>4.9.3 中缀表达式转后缀表达式</h3><p>我们把平时所用的标准四则运算表达式，即“<code>9+(3-1)×3+10÷2</code>”叫做中缀表达式。因为所有的运算符号都在两数字的中间。<br>中缀表达式“<code>9+(3-1)×3+10÷2</code>”转化为后缀表达式“<code>9 3 1-3*+10 2/+</code>”。<br>规则：<strong>从左到右遍历中缀表达式的每个数字和符号，若是数字就输出，即成为后缀表达式的一部分；若是符号，则判断其与栈顶符号的优先级，是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出，并将当前符号进栈，一直到最终输出后缀表达式为止。</strong>  </p>
<h2 id="4-10-队列的定义"><a href="#4-10-队列的定义" class="headerlink" title="4.10 队列的定义"></a>4.10 队列的定义</h2><p>队列(queue)是只允许在一端进行插入操作，而在另一端进行删除操作的线性表。<br>队列是一种先进先出(First In First Out)的线性表，简称FIFO。允许插入的一端称为队尾，允许删除的一端称为队头。  </p>
<h2 id="4-11-队列的抽象数据类型"><a href="#4-11-队列的抽象数据类型" class="headerlink" title="4.11 队列的抽象数据类型"></a>4.11 队列的抽象数据类型</h2><figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c">ADT 队列(Queue)<br>Data<br>    同线性表。元素具有相同的类型，相邻元素具有前驱和后继关系。<br><span class="hljs-function">Operation</span><br><span class="hljs-function">    <span class="hljs-title">InitQueue</span><span class="hljs-params">(*Q)</span>：初始化操作，建立一个空队列Q。</span><br><span class="hljs-function">    <span class="hljs-title">DestroyQueue</span><span class="hljs-params">(*Q)</span>：若队列Q存在，则销毁它。</span><br><span class="hljs-function">    <span class="hljs-title">ClearQueue</span><span class="hljs-params">(*Q)</span>：将队列Q清空。</span><br><span class="hljs-function">    <span class="hljs-title">QueueEmpty</span><span class="hljs-params">(Q)</span>：若队列Q为空，返回<span class="hljs-literal">true</span>，否则返回<span class="hljs-literal">false</span>。</span><br><span class="hljs-function">    <span class="hljs-title">GetHead</span><span class="hljs-params">(Q，*e)</span>：若队列Q存在且非空，用e返回队列Q的队头元素。</span><br><span class="hljs-function">    <span class="hljs-title">EnQueue</span><span class="hljs-params">(*Q，e)</span>：若队列Q存在，插入新元素e到队列Q中并成为队尾元素。</span><br><span class="hljs-function">    <span class="hljs-title">DeQueue</span><span class="hljs-params">(*Q，*e)</span>：删除队列Q中队头元素，并用e返回其值。</span><br><span class="hljs-function">    <span class="hljs-title">QueueLength</span><span class="hljs-params">(Q)</span>：返回队列Q的元素个数</span><br><span class="hljs-function">endADT</span><br></code></pre></div></td></tr></table></figure>

<h2 id="4-12-循环队列"><a href="#4-12-循环队列" class="headerlink" title="4.12 循环队列"></a>4.12 循环队列</h2><h3 id="4-12-1-队列顺序存储的不足"><a href="#4-12-1-队列顺序存储的不足" class="headerlink" title="4.12.1 队列顺序存储的不足"></a>4.12.1 队列顺序存储的不足</h3><p>入队的时间复杂度为O(1)。<br>与栈不同的是，队列元素的出列是在队头，即下标为0的位置，那也就意味着，队列中的所有元素都得向前移动，以保证队列的队头，也就是下标为0的位置不为空，此时时间复杂度为出队的时间复杂度为O(n)，效率太低。<br>如果队列前面的位置空的，后面的位置排满了，那么新进的元素可以排到前面，这就引进了循环队列的概念。<br>为了避免当只有一个元素时，队头和队尾重合使处理变得麻烦，所以引入两个指针，front指针指向队头元素，rear指针指向队尾元素的下一个位置，这样当front等于rear时，此队列是空队列。  </p>
<h3 id="4-12-2-循环队列定义"><a href="#4-12-2-循环队列定义" class="headerlink" title="4.12.2 循环队列定义"></a>4.12.2 循环队列定义</h3><p>队列中头尾相接的顺序存储结构称为循环队列。<br>此时问题又出来了，空队列时，front等于rear，现在当队列满时，也是front等于rear，那么如何判断此时的队列究竟是空还是满呢？</p>
<ol>
<li>办法一是设置一个标志变量flag，当<code>front==rear</code>，且flag=0时为队列空，当<code>front==rear</code>，且flag=1时为队列满。</li>
<li>办法二是当队列空时，条件就是<code>front=rear</code>，当队列满时，我们修改其条件，保留一个元素空间。也就是说，队列满时，数组中还有一个空闲单元。<br>我们重点来讨论第二种方法，由于rear可能比front大，也可能比front小，所以尽管它们只相差一个位置时就是满的情况，但也可能是相差整整一圈。所以若队列的最大尺寸为QueueSize，那么队列满的条件是“<code>(rear+1)%QueueSize==front</code>”(取模“%”的目的就是为了整合rear与front大小为一个问题)。<br>通用的计算队列长度公式为：<code>(rear-front+QueueSize)%QueueSize</code>。<br>循环队列的顺序存储结构代码如下：</li>
</ol>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">int</span> QElemType; <span class="hljs-comment">/* QElemType类型根据实际情况而定，这里假设为int */</span><br><span class="hljs-comment">/* 循环队列的顺序存储结构 */</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span></span><br><span class="hljs-class">&#123;</span><br>    QElemType data[MAXSIZE];<br>    <span class="hljs-keyword">int</span> front;      <span class="hljs-comment">/* 头指针 */</span><br>    <span class="hljs-keyword">int</span> rear;       <span class="hljs-comment">/* 尾指针，若队列不空，指向队列尾元素的下一个位置 */</span><br>&#125;SqQueue;<br></code></pre></div></td></tr></table></figure>

<p>循环队列的初始化代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 初始化一个空队列Q */</span><br><span class="hljs-function">Status <span class="hljs-title">InitQueue</span><span class="hljs-params">(SqQueue *Q)</span></span><br><span class="hljs-function"></span>&#123;<br>    Q-&gt;front = <span class="hljs-number">0</span>;<br>    Q-&gt;rear = <span class="hljs-number">0</span>;<br>    <span class="hljs-keyword">return</span>  OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>循环队列求队列长度代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 返回Q的元素个数，也就是队列的当前长度 */</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">QueueLength</span><span class="hljs-params">(SqQueue Q)</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">return</span>  (Q.rear - Q.front + MAXSIZE) % MAXSIZE;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>循环队列的入队列操作代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 若队列未满，则插入元素e为Q新的队尾元素 */</span><br><span class="hljs-function">Status <span class="hljs-title">EnQueue</span><span class="hljs-params">(SqQueue *Q, QElemType e)</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">if</span> ((Q-&gt;rear + <span class="hljs-number">1</span>) % MAXSIZE == Q-&gt;front)    <span class="hljs-comment">/* 队列满的判断 */</span><br>        <span class="hljs-keyword">return</span> ERROR;<br>    Q-&gt;data[Q-&gt;rear] = e;               <span class="hljs-comment">/* 将元素e赋值给队尾 */</span><br>    Q-&gt;rear = (Q-&gt;rear + <span class="hljs-number">1</span>) % MAXSIZE;<span class="hljs-comment">/* rear指针向后移一位置， */</span><br>                                      <span class="hljs-comment">/* 若到最后则转到数组头部 */</span><br>    <span class="hljs-keyword">return</span>  OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>循环队列的出队列操作代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 若队列不空，则删除Q中队头元素，用e返回其值 */</span><br><span class="hljs-function">Status <span class="hljs-title">DeQueue</span><span class="hljs-params">(SqQueue *Q, QElemType *e)</span></span><br><span class="hljs-function"></span>&#123;<br>    <span class="hljs-keyword">if</span> (Q-&gt;front == Q-&gt;rear)            <span class="hljs-comment">/* 队列空的判断 */</span><br>        <span class="hljs-keyword">return</span> ERROR;<br>    *e = Q-&gt;data[Q-&gt;front];                <span class="hljs-comment">/* 将队头元素赋值给e */</span><br>    Q-&gt;front = (Q-&gt;front + <span class="hljs-number">1</span>) % MAXSIZE;    <span class="hljs-comment">/* front指针向后移一位置 */</span><br>                                    <span class="hljs-comment">/* 若到最后则转到数组头部 */</span><br>    <span class="hljs-keyword">return</span>  OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h2 id="4-13-队列的链式存储结构及实现"><a href="#4-13-队列的链式存储结构及实现" class="headerlink" title="4.13 队列的链式存储结构及实现"></a>4.13 队列的链式存储结构及实现</h2><p>队列的链式存储结构，其实就是线性表的单链表，只不过它只能尾进头出而已，我们把它简称为链队列。<br>为了操作上的方便，我们将队头指针指向链队列的头结点，而队尾指针指向终端结点，如图4-13-1所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-13-1.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-13-1"><br>空队列时，front和rear都指向头结点，如图4-13-2所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-13-2.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-13-2"><br>链队列的结构为：  </p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">int</span> QElemType; <span class="hljs-comment">/* QElemType类型根据实际情况而定，这里假设为int */</span><br><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">QNode</span>    /* 结点结构 */</span><br><span class="hljs-class">&#123;</span><br>    QElemType data;<br>    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">QNode</span> *<span class="hljs-title">next</span>;</span><br>&#125;QNode, *QueuePtr;<br><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span>            /* 队列的链表结构 */</span><br><span class="hljs-class">&#123;</span><br>    QueuePtr front, rear; <span class="hljs-comment">/* 队头、队尾指针 */</span><br>&#125;LinkQueue;<br></code></pre></div></td></tr></table></figure>

<h3 id="4-13-1-队列的链式存储结构——入队操作"><a href="#4-13-1-队列的链式存储结构——入队操作" class="headerlink" title="4.13.1 队列的链式存储结构——入队操作"></a>4.13.1 队列的链式存储结构——入队操作</h3><p>入队操作时，其实就是在链表尾部插入结点，如图4-13-3所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-13-3.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-13-3"><br>入队代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 插入元素e为Q的新的队尾元素 */</span><br><span class="hljs-function">Status <span class="hljs-title">EnQueue</span><span class="hljs-params">(LinkQueue *Q, QElemType e)</span></span><br><span class="hljs-function"></span>&#123;<br>    QueuePtr s = (QueuePtr)<span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(QNode));<br>    <span class="hljs-keyword">if</span> (!s) <span class="hljs-comment">/* 存储分配失败 */</span><br>        <span class="hljs-built_in">exit</span>(OVERFLOW);<br>    s-&gt;data = e;<br>    s-&gt;next = <span class="hljs-literal">NULL</span>;<br>    Q-&gt;rear-&gt;next = s;    <span class="hljs-comment">/* 把拥有元素e的新结点s赋值给原队尾结点的后继，见图中① */</span><br>    Q-&gt;rear = s;        <span class="hljs-comment">/* 把当前的s设置为队尾结点，rear指向s，见图中② */</span><br>    <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h3 id="4-13-2-队列的链式存储结构——出队操作"><a href="#4-13-2-队列的链式存储结构——出队操作" class="headerlink" title="4.13.2 队列的链式存储结构——出队操作"></a>4.13.2 队列的链式存储结构——出队操作</h3><p>出队操作时，就是头结点的后继结点出队，将头结点的后继改为它后面的结点，若链表除头结点外只剩一个元素时，则需将rear指向头结点，如图4-13-4所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-13-4.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-13-4">  </p>
<p>出队代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs c"><span class="hljs-comment">/* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */</span><br><span class="hljs-function">Status <span class="hljs-title">DeQueue</span><span class="hljs-params">(LinkQueue *Q, QElemType *e)</span></span><br><span class="hljs-function"></span>&#123;<br>    QueuePtr p;<br>    <span class="hljs-keyword">if</span> (Q-&gt;front == Q-&gt;rear)<br>        <span class="hljs-keyword">return</span> ERROR;<br>    p = Q-&gt;front-&gt;next;        <span class="hljs-comment">/* 将欲删除的队头结点暂存给p，见图中① */</span><br>    *e = p-&gt;data;                <span class="hljs-comment">/* 将欲删除的队头结点的值赋值给e */</span><br>    Q-&gt;front-&gt;next = p-&gt;next;<span class="hljs-comment">/* 将原队头结点的后继p-&gt;next赋值给头结点后继，见图中② */</span><br>    <span class="hljs-keyword">if</span> (Q-&gt;rear == p)<span class="hljs-comment">/* 空队列的时候 */</span> <span class="hljs-comment">/* 若队头就是队尾，则删除后将rear指向头结点，见图中③ */</span><br>        Q-&gt;rear = Q-&gt;front;<br>    <span class="hljs-built_in">free</span>(p);<br>    <span class="hljs-keyword">return</span> OK;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>对于循环队列与链队列的比较，可以从两方面来考虑，从时间上，其实它们的基本操作都是常数时间，即都为O（1）的，不过循环队列是事先申请好空间，使用期间不释放，而对于链队列，每次申请和释放结点也会存在一些时间开销，如果入队出队频繁，则两者还是有细微差异。对于空间上来说，循环队列必须有一个固定的长度，所以就有了存储元素个数和空间浪费的问题。而链队列不存在这个问题，尽管它需要一个指针域，会产生一些空间上的开销，但也可以接受。所以在空间上，链队列更加灵活。<br>总的来说，在可以确定队列长度最大值的情况下，建议用循环队列，如果你无法预估队列的长度时，则用链队列。  </p>
<h2 id="4-14-总结回顾"><a href="#4-14-总结回顾" class="headerlink" title="4.14 总结回顾"></a>4.14 总结回顾</h2><p>这一章讲的是栈和队列，它们都是特殊的线性表，只不过对插入和删除操作做了限制。<br>栈(stack)是限定仅在表尾进行插入和删除操作的线性表。<br>队列(queue)是只允许在一端进行插入操作，而在另一端进行删除操作的线性表。<br>它们均可以用线性表的顺序存储结构来实现，但都存在着顺序存储的一些弊端。因此它们各自有各自的技巧来解决这个问题。<br>对于栈来说，如果是两个相同数据类型的栈，则可以用数组的两端作栈底的方法来让两个栈共享数据，这就可以最大化地利用数组的空间。<br>对于队列来说，为了避免数组插入和删除时需要移动数据，于是就引入了循环队列，使得队头和队尾可以在数组中循环变化。解决了移动数据的时间损耗，使得本来插入和删除是O(n)的时间复杂度变成了O(1)。<br>它们也都可以通过链式存储结构来实现，实现原则上与线性表基本相同如图4-14-1所示。<br><img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%AC%AC%E5%9B%9B%E7%AB%A0/4-14-1.JPG?raw=true" srcset="/img/loading.gif" lazyload alt="4-14-1">  </p>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
                    
                      <a class="hover-with-bg" href="/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">大话数据结构</a>
                    
                  </div>
                
                
                  <div class="post-meta">
                    <i class="iconfont icon-tags"></i>
                    
                      <a class="hover-with-bg" href="/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</a>
                    
                      <a class="hover-with-bg" href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构</a>
                    
                      <a class="hover-with-bg" href="/tags/%E6%A0%88%E5%92%8C%E9%98%9F%E5%88%97/">栈和队列</a>
                    
                  </div>
                
              </div>
              
                <p class="note note-warning">
                  
                    本博客所有文章除特别声明外，均采用 <a target="_blank" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！
                  
                </p>
              
              
                <div class="post-prevnext">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2019/12/01/hexo/hexo%E4%B8%8A%E4%BC%A0%E9%83%A8%E7%BD%B2%E5%91%BD%E4%BB%A4/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">hexo上传部署命令</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2019/11/28/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%AC%94%E8%AE%B0/%E7%AC%AC3%E7%AB%A0%20%E7%BA%BF%E6%80%A7%E8%A1%A8/">
                        <span class="hidden-mobile">大话数据结构第三章 线性表</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
              <!-- Comments -->
              <article class="comments" id="comments" lazyload>
                
                  
                
                
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js', function () {
        new Valine({
          el: "#valine",
          app_id: "YzLqNtMw1YEwwACli1FUsIUM-gzGzoHsz",
          app_key: "HLUt5izfTvTcbEbOrA59W92a",
          placeholder: "畅所欲言...",
          path: window.location.pathname,
          avatar: "robohash",
          meta: ["nick","mail","link"],
          pageSize: "10",
          lang: "zh-CN",
          highlight: true,
          recordIP: false,
          serverURLs: "",
        });
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


              </article>
            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->

  <div class="col-lg-7 mx-auto nopadding-x-md">
    <div class="container custom post-custom mx-auto">
      <img src="https://closer_laps.coding.net/p/picture/d/picture/git/raw/master/pay/pay.png" srcset="/img/loading.gif" lazyload class="rounded mx-auto d-block mt-3" style="width:355.4px; height:200px;">
    </div>
  </div>


    

    
      <a id="scroll-top-button" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
    

    
  </main>

  <footer class="text-center mt-5 py-3">
  <div class="footer-content">
     <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
  </div>
  
  <div class="statistics">
    
    

    
      
        <!-- LeanCloud 统计PV -->
        <span id="leancloud-site-pv-container" style="display: none">
            总访问量 
            <span id="leancloud-site-pv"></span>
             次
          </span>
      
      
        <!-- LeanCloud 统计UV -->
        <span id="leancloud-site-uv-container" style="display: none">
            总访客数 
            <span id="leancloud-site-uv"></span>
             人
          </span>
      

    
  </div>


  
  <!-- 备案信息 -->
  <div class="beian">
    <span>
      <a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">
        苏ICP备20032307号
      </a>
    </span>
    
      
        <span>
          <a
            href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=32020602001023"
            rel="nofollow noopener"
            class="beian-police"
            target="_blank"
          >
            
              <span style="visibility: hidden; width: 0">|</span>
              <img src="/img/police_beian.png" srcset="/img/loading.gif" lazyload alt="police-icon"/>
            
            <span>苏公网安备 32020602001023号</span>
          </a>
        </span>
      
    
  </div>


  
</footer>


  <!-- SCRIPTS -->
  
  <script  src="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" ></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>

<!-- Plugins -->


  
    <script  src="/js/img-lazyload.js" ></script>
  



  



  <script  src="https://cdn.jsdelivr.net/npm/tocbot@4.12.2/dist/tocbot.min.js" ></script>



  <script  src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js" ></script>



  <script  src="https://cdn.jsdelivr.net/npm/anchor-js@4.3.0/anchor.min.js" ></script>



  <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2.0.8/dist/clipboard.min.js" ></script>




  <script defer src="/js/leancloud.js" ></script>



  <script  src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var title = document.getElementById('subtitle').title;
      
      typing(title)
      
    })(window, document);
  </script>



  <script  src="/js/local-search.js" ></script>
  <script>
    (function () {
      var path = "/local-search.xml";
      $('#local-search-input').on('click', function() {
        searchFunc(path, 'local-search-input', 'local-search-result');
      });
      $('#modalSearch').on('shown.bs.modal', function() {
        $('#local-search-input').focus();
      });
    })()
  </script>





  

  
    <!-- MathJax -->
    <script>
      MathJax = {
        tex: {
          inlineMath: [['$', '$'], ['\\(', '\\)']]
        },
        options: {
          renderActions: {
            findScript: [10, doc => {
              document.querySelectorAll('script[type^="math/tex"]').forEach(node => {
                const display = !!node.type.match(/; *mode=display/);
                const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
                const text = document.createTextNode('');
                node.parentNode.replaceChild(text, node);
                math.start = { node: text, delim: '', n: 0 };
                math.end = { node: text, delim: '', n: 0 };
                doc.math.push(math);
              });
            }, '', false],
            insertedScript: [200, () => {
              document.querySelectorAll('mjx-container').forEach(node => {
                let target = node.parentNode;
                if (target.nodeName.toLowerCase() === 'li') {
                  target.parentNode.classList.add('has-jax');
                }
              });
            }, '', false]
          }
        }
      };
    </script>

    <script async src="https://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js" ></script>

  








  
    <!-- Baidu Analytics -->
    <script defer>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?608f2baddd361128381ad2bf9377bf89";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>
  

  

  

  

  

  





<!-- 主题的启动项 保持在最底部 -->
<script  src="/js/boot.js" ></script>


</body>
</html>
